package com.xbongbong.pro.service.toolbox.statistic.help;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasAppEntity;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasFormRefEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.BIProductReformEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.field.LinkListPojo;
import com.xbongbong.paas.field.MultiUnitItemPoJo;
import com.xbongbong.paas.field.UnitItemPoJo;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.subform.TransferSubFormHelper;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.model.PaasFormRefModel;
import com.xbongbong.paas.pojo.ProductFieldPojo;
import com.xbongbong.paas.pojo.SummaryDataPoJo;
import com.xbongbong.paas.pojo.dto.CoOpPermsAddDTO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.redis.RedisTimeConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.SymbolConstant;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.statistic.pojo.vo.ContractInfoVO;
import com.xbongbong.pro.statistic.pojo.vo.CustomerInfoVO;
import com.xbongbong.pro.statistic.pojo.vo.EntityInfoVO;
import com.xbongbong.pro.statistic.result.pojo.ChartFindSheetPojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultBasePojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPojo;
import com.xbongbong.pro.statistic.result.pojo.CustomerOkFirstSecondPojo;
import com.xbongbong.pro.statistic.result.pojo.PanoramaPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.ContractPerformanceEntity;
import com.xbongbong.saas.domain.entity.ContractUserEntity;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.ProductCategoryEntity;
import com.xbongbong.saas.domain.entity.ProductUnitGroupEntity;
import com.xbongbong.saas.domain.entity.ProductUnitRateEntity;
import com.xbongbong.saas.domain.entity.WorkOrderEntity;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.WorkOrderForStatisticsEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.AllFieldEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.OrderEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.ProductionOrderEnum;
import com.xbongbong.saas.enums.business.ReturnEnum;
import com.xbongbong.saas.enums.business.WorkOrderEnum;
import com.xbongbong.saas.enums.subform.ContractPerformanceSubFormEnum;
import com.xbongbong.saas.enums.subform.ContractTeamEnum;
import com.xbongbong.saas.enums.subform.SubFormTransferEnum;
import com.xbongbong.saas.help.FundHelp;
import com.xbongbong.saas.model.IndexTypeModel;
import com.xbongbong.saas.model.InstockProductModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.model.ProductCategoryModel;
import com.xbongbong.saas.model.ProductUnitGroupModel;
import com.xbongbong.saas.model.ProductUnitRateModel;
import com.xbongbong.saas.model.SpecificationModel;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.model.UserModel;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getJsonArrOrDefaultFromFormData;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * 用于查询业务列表的帮助类
 * @author zcp
 * @version v1.0
 * @date 2019/4/5 17:43
 * @since v1.0
 */
@Component
public class FormHelp {
    private static final Logger LOG = LoggerFactory.getLogger(FormHelp.class);
    @Resource
    private EsHelper esHelper;
    @Resource
    private ChartQueryHelp chartQueryHelp;
    @Resource
    private ChartDtoHelp chartDtoHelp;
    @Resource
    private ProductCategoryModel productCategoryModel;
    @Resource
    private InstockProductModel instockProductModel;
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private SpecificationModel specificationModel;
    @Resource
    private TransferSubFormHelper transferSubFormHelper;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private ProductUnitRateModel productUnitRateModel;
    @Resource
    private ProductUnitGroupModel productUnitGroupModel;
    @Resource
    private UserModel userModel;
    @Resource
    private PaasAppModel paasAppModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private PaasFormRefModel paasFormRefModel;
    @Resource
    private IndexTypeModel indexTypeModel;

    //-------------------------------获取表单信息相关方法-------------------------------
    /**
     * 获取表单的Id（es内为dataId）列表
     *
     * @param corpid 强制塞入公司id，防止忘记塞入公司id
     * @param boolQueryBuilder 入参，查询条件（注，封装条件时一定要把corpid条件置入）
     * @param indexTypeEnum 索引，用于获取index和type
     * @return java.util.List<java.lang.Long>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/5 18:12
     * @since v1.0
     * @version v1.0
     */
    public Set<Long> getDataIdList(String corpid, BoolQueryBuilder boolQueryBuilder, IndexTypeEnum indexTypeEnum) throws XbbException {
        List<String> fieldList = Arrays.asList("id", "dataId");
        List<PaasFormDataEntityExt> list = getFormList(corpid, boolQueryBuilder, indexTypeEnum, fieldList);
        Set<Long> dataIdIn = new HashSet<>();
        for (PaasFormDataEntityExt entity : list) {
            dataIdIn.add(entity.getId());
        }
        return dataIdIn;
    }

    /**
     * 获取表单列表
     *
     * @param corpid 强制塞入公司id，防止忘记塞入公司id
     * @param boolQueryBuilder 入参，查询条件（注，封装条件时一定要把corpid条件置入）
     * @param indexTypeEnum 索引，用于获取index和type
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/5/13 21:49
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getFormList(String corpid, BoolQueryBuilder boolQueryBuilder, IndexTypeEnum indexTypeEnum, List<String> fieldList) throws XbbException {
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        List<PaasFormDataEntityExt> list = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        return list;
    }

    /**
     * 以Id列表获取表单列表（通过ids查询表单、未限制del）
     * @param corpid 公司id
     * @param indexTypeEnum 索引枚举
     * @param idIn 表单ids
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/6 18:08
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getFormListByIds4All(String corpid, IndexTypeEnum indexTypeEnum, Collection<Long> idIn, List<String> fieldList) throws XbbException {
        if (idIn == null || idIn.size() == 0) {
            return new ArrayList<>();
        }
        //筛选条件,
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid, null);
        boolQueryBuilder.filter(termsQuery("id", idIn));
        return esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }

    /**
     * 以Id列表和formId列表获取表单列表（通过ids查询表单、未限制del）
     * @param corpid 公司id
     * @param indexTypeEnum 索引枚举
     * @param idIn 表单ids
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author weiming
     * @date 2019/4/6 18:08
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getFormListByIds4Distributor(List<Long> formIdList, String corpid, IndexTypeEnum indexTypeEnum, Collection<Long> idIn, List<String> fieldList) throws XbbException {
        if (idIn == null || idIn.size() == 0) {
            return new ArrayList<>();
        }
        //筛选条件,
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid, null);
        if (IndexTypeEnum.IDX_SAAS_REFUND == indexTypeEnum){
            boolQueryBuilder.filter(termsQuery(ReturnEnum.getAttrConnectData(ReturnEnum.RED_CONTRACT_ID), idIn));
        }else{
            boolQueryBuilder.filter(termsQuery("id", idIn));
        }
        if (formIdList != null ){
            boolQueryBuilder.filter(termsQuery("formId", formIdList));
        }
        return esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }
    /**
     * 以Id列表获取表单列表（通过ids查询表单、未限制del）
     * @param corpid 公司id
     * @param indexTypeEnum 索引枚举
     * @param idIn 表单ids
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @param clazz 实体class
     * @return java.util.List<T>
     * @throws XbbException
     * @author zcp
     * @date 2019/5/5 16:07
     * @since v1.0
     * @version v1.0
     */
    public <T> List<T> getFormListByIds4All(String corpid, IndexTypeEnum indexTypeEnum, Collection<Long> idIn, List<String> fieldList, Class<T> clazz) throws XbbException {
        //筛选条件
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid, null);
        return getFormListByIds4All(indexTypeEnum, boolQueryBuilder, "id", idIn, fieldList, clazz);
    }

    /**
     * 以Id列表获取表单列表（通过ids查询表单、其他条件塞入boolQueryBuilder）
     * @param indexTypeEnum 索引枚举
     * @param boolQueryBuilder 查询条件，一定要确保塞入了corpid条件
     * @param attr 限制idIn字段attr
     * @param idIn 表单ids
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @param clazz 实体class
     * @return java.util.List<T>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public <T> List<T> getFormListByIds4All(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, String attr, Collection<Long> idIn, List<String> fieldList, Class<T> clazz) throws XbbException {
        if (StringUtil.isEmpty(attr) || CollectionUtils.isEmpty(idIn)) {
            return new ArrayList<>();
        }
        idIn.remove(null);
        boolQueryBuilder.filter(termsQuery(attr, idIn));
        return esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, clazz, fieldList);
    }

    /**
     * 查询出业务list并构建业务id和字段attr的对应关系
     * @param attr 字段名
     * @param corpid 公司id
     * @param indexTypeEnum 索引枚举
     * @param idIn 表单ids
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @return java.util.Map<java.lang.String,java.lang.String>
     * @throws XbbException
     * @author zcp
     * @date 2019/5/2 17:00
     * @since v1.0
     * @version v1.0
     */
    public Map<Long, String> getFormIdAttrMapByIds4All(String attr, String corpid, IndexTypeEnum indexTypeEnum, Collection<Long> idIn, List<String> fieldList) throws XbbException {
        //查询出表单list，用于获取业务名
        List<PaasFormDataEntityExt> list = getFormListByIds4All(corpid, indexTypeEnum, idIn, fieldList);
        //id-字段attr的值的对应关系map
        Map<Long, String> idAttrMap = new HashMap<>(list.size());
        for (PaasFormDataEntityExt entityExt : list) {
            Long dataId = entityExt.getId();
            if (BasicConstant.SERIAL_NO.equals(attr)) {
                //编号直接读实体的编号
                idAttrMap.put(dataId, entityExt.getSerialNo());
            } else {
                JSONObject data = entityExt.getData();
                if (data == null) {
                    continue;
                }
                idAttrMap.put(dataId, data.getString(attr));
            }
        }
        return idAttrMap;
    }

    /**
     * 查询出订单一些数据
     * @param corpid 公司id
     * @param indexTypeEnum 索引枚举
     * @param idIn 表单ids
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @return java.util.Map<java.lang.String,java.lang.String>
     * @throws XbbException
     * @author weiming
     * @date 2019/5/2 17:00
     * @since v1.0
     * @version v1.0
     */
    public Map<Long, JSONObject> getOrderDataByIds4Distributor(List<Long> formIdList, String corpid, IndexTypeEnum indexTypeEnum, Collection<Long> idIn, List<String> fieldList) throws XbbException {
        //查询出表单list，用于获取业务名
        List<PaasFormDataEntityExt> list = getFormListByIds4Distributor(formIdList, corpid, indexTypeEnum, idIn, fieldList);
        //id-字段attr的值的对应关系map
        Map<Long, JSONObject> idObjectMap = new HashMap<>(list.size());
        for (PaasFormDataEntityExt entityExt : list) {
            Long dataId = entityExt.getId();
            JSONObject jsonObject = new JSONObject();
            //编号直接读实体的编号
            jsonObject.put("orderNo", entityExt.getSerialNo());
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            jsonObject.put("distributorName", data.getString(OrderEnum.LINK_CUSTOMER_HIDE.getAttr()));
            jsonObject.put("distributorId", data.getString(OrderEnum.LINK_DISTRIBUTOR.getAttr()));
            jsonObject.put("signTime", data.getString(OrderEnum.SIGN_TIME.getAttr()));
            idObjectMap.put(dataId, jsonObject);
        }
        return idObjectMap;
    }

    /**
     * 查询退货单数据
     * @param corpid 公司id
     * @param indexTypeEnum 索引枚举
     * @param idIn 表单ids
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @return java.util.Map<java.lang.String,java.lang.String>
     * @throws XbbException
     * @author weiming
     * @date 2019/5/2 17:00
     * @since v1.0
     * @version v1.0
     */
    public Map<Long, JSONObject> getReturnDataByIds4Distributor(List<Long> formIdList, String corpid, IndexTypeEnum indexTypeEnum, Collection<Long> idIn, List<String> fieldList) throws XbbException {
        //查询出表单list，用于获取业务名
        List<PaasFormDataEntityExt> list = getFormListByIds4Distributor(formIdList, corpid, indexTypeEnum, idIn, fieldList);
        //id-字段attr的值的对应关系map
        Map<Long, JSONObject> idObjectMap = new HashMap<>(list.size());
        for (PaasFormDataEntityExt entityExt : list) {
            Long dataId = entityExt.getId();
            JSONObject jsonObject = new JSONObject();
            //编号直接读实体的编号
            jsonObject.put("returnNo", entityExt.getSerialNo());
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            Long redContractId = data.getLong(ReturnEnum.RED_CONTRACT_ID.getAttr());
            jsonObject.put("returnName", data.getString(ReturnEnum.RETURN_NAME.getAttr()));
            jsonObject.put("linkDistributor", data.getString(ReturnEnum.LINK_DISTRIBUTOR_LINKED_TEXT.getAttr()));
            jsonObject.put("linkDistributorId", data.getLong(ReturnEnum.LINK_DISTRIBUTOR.getAttr()));
            jsonObject.put("linkOrder", data.getString(ReturnEnum.ORDER_ID_LINKED_TEXT.getAttr()));
            jsonObject.put("signDate", data.getLong(ReturnEnum.SIGN_DATE.getAttr()));
            jsonObject.put("orderId", data.getLong(ReturnEnum.ORDER_ID.getAttr()));
            jsonObject.put("dataId",dataId);
            idObjectMap.put(redContractId, jsonObject);
        }
        return idObjectMap;
    }

    /**
     * 查询出业务list并构建业务id和多个字段attr的对应关系
     *
     * @param attrs 字段名的集合
     * @param corpid 公司id
     * @param indexTypeEnum 索引
     * @param idIn 数据id集合
     * @param fieldList 要查询的字段
     * @return java.util.Map<java.lang.Long,java.util.Map<java.lang.String,java.lang.String>>
     * @author 徐俊杰
     * @date 2020/2/19 14:37
     * @since v1.0
     */
    Map<Long, Map<String, String>> getIdAttrMapsByIds(Set<String> attrs, String corpid, IndexTypeEnum indexTypeEnum, Collection<Long> idIn, List<String> fieldList) throws XbbException {
        //查询出表单list，用于获取业务名
        List<PaasFormDataEntityExt> list = getFormListByIds4All(corpid, indexTypeEnum, idIn, fieldList);
        //id-字段attr的值的对应关系map
        Map<Long, Map<String, String>> idAttrMap = new HashMap<>(list.size());
        for (PaasFormDataEntityExt entityExt : list) {
            Map<String, String> attrValueMap = new HashMap<>(attrs.size());
            Long dataId = entityExt.getId();
            for (String attr : attrs) {
                if (BasicConstant.SERIAL_NO.equals(attr)) {
                    //编号直接读实体的编号
                    attrValueMap.put(attr, entityExt.getSerialNo());
                } else {
                    JSONObject data = entityExt.getData();
                    if (data == null) {
                        continue;
                    }
                    attrValueMap.put(attr, data.getString(attr));
                }
            }
            idAttrMap.put(dataId, attrValueMap);
        }
        return idAttrMap;
    }
    /**
     * 得到productId和产品信息的对应关系map，其中产品信息格式为：产品名称(规格：xxx)
     * @param corpid 公司id
     * @param idIn 产品id合集
     * @return java.util.Map<java.lang.Long,java.lang.String>
     * @throws XbbException
     * @author zcp
     * @date 2019/7/5 15:43
     * @since v1.0
     * @version v1.0
     */
    public Map<Long, String> getProductIdInfoMapByIds(String corpid, Collection<Long> idIn) throws XbbException {
        ProductEnum enumName = ProductEnum.NAME;
        ProductEnum enumSpecification = ProductEnum.SPECIFICATION_LINK_TEXT;
        List<String> fieldList = Arrays.asList("id", "dataId", ProductEnum.getAttrConnectData(enumName), ProductEnum.getAttrConnectData(enumSpecification));
        String attrName = enumName.getAttr();
        String attrSpecification = enumSpecification.getAttr();

        //查询出表单list，用于获取业务名
        List<PaasFormDataEntityExt> list = getFormListByIds4All(corpid, IndexTypeEnum.IDX_SAAS_PRODUCT, idIn, fieldList);
        //id-字段attr的值的对应关系map
        Map<Long, String> idInfoMap = new HashMap<>(list.size());
        StringBuilder sb;
        for (PaasFormDataEntityExt entityExt : list) {
            Long dataId = entityExt.getId();
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            String name = data.getString(attrName);
            String specification = AllFieldEnum.SPECIFICATION.getAttrName() + "：" + specificationModel.joinSpecification(data.getString(attrSpecification));
            sb = new StringBuilder();
            sb.append(name).append(String.format(SymbolConstant.BRACKETS_ENGLISH, specification));
            idInfoMap.put(dataId, sb.toString());
        }
        return idInfoMap;
    }

    /**
     * 构建产品id和entity的对应关系
     *
     * @param formList 表单列表（从es查询出的列表）
     * @return java.util.Map<java.lang.Long,com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/9 11:17
     * @since v1.0
     * @version v1.0
     */
    public Map<Long, PaasFormDataEntityExt> getMap4IdEntity(List<PaasFormDataEntityExt> formList) {
        //表单id--表单实体
        Map<Long, PaasFormDataEntityExt> map = new HashMap<>(formList.size());
        for (PaasFormDataEntityExt entity : formList) {
            map.put(entity.getDataId(), entity);
        }
        return map;
    }
    /**
     * 只查询出未删除的：以Id列表获取表单列表（通过ids查询表单、未限制del）
     * @param corpid 公司id
     * @param indexTypeEnum 索引枚举
     * @param idIn 表单ids
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/6 19:49
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getFormListByIds4Normal(String corpid, IndexTypeEnum indexTypeEnum, Collection<Long> idIn, List<String> fieldList) throws XbbException {
        if (idIn == null || idIn.size() == 0) {
            return new ArrayList<>();
        }
        //筛选条件
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termsQuery("id", idIn));
        return esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }

    /**
     * 通过ids获取负责人团队
     * @param indexTypeEnum 索引
     * @param corpid 公司id
     * @param idIn 要查询业务团队的ids
     * @param isMain 是否是主负责人
     * @param targetClass 团队实体class
     * @return java.util.List<T>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/30 14:25
     * @since v1.0
     * @version v1.0
     */
    public <T> List<T> getUserTeamListByIds(IndexTypeEnum indexTypeEnum, String corpid, Collection<Long> idIn, Integer isMain, Class<T> targetClass) throws XbbException {
        if (idIn == null || idIn.size() == 0) {
            return new ArrayList<>();
        }
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4BaseByIds(corpid, DelEnum.NORMAL.getDel(), idIn, indexTypeEnum);
        if (isMain != null) {
            boolQueryBuilder.filter(termQuery(SubFormTransferEnum.getEsAttrByIndexTypeAndDbAttr(indexTypeEnum, "isMain"), isMain));
        }
        //查询团队list
        return esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, targetClass, null);
    }

    //-------------------------------获取客户表单（id）list相关-------------------------------
    /**
     * 获取客户信息，如id、name
     * @param corpid 公司id
     * @param customerIdIn 客户id数组
     * @return java.util.List<com.xbongbong.pro.statistic.pojo.vo.CustomerDetailVO>
     * @throws XbbException
     * @author zcp
     * @date 2019/2/28 22:08
     * @since v1.0
     * @version v1.0
     */
    public List<CustomerInfoVO> getCustomerListByIds(String corpid, Collection<Long> customerIdIn) throws XbbException {
        List<CustomerInfoVO> customerInfoVOList = new ArrayList<>();
        if (customerIdIn == null || customerIdIn.size() == 0) {
            return customerInfoVOList;
        }
        //只读取出必要的字段
        CustomerManagementEnum nameEnum = CustomerManagementEnum.NAME;
        String dataName = CustomerManagementEnum.getAttrConnectData(nameEnum);
        List<String> fieldList = Arrays.asList("id", "dataId", dataName);
        IndexTypeEnum indexTypeEnum = indexTypeModel.getCustomerIndex(corpid, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), SaasMarkEnum.SAAS.getCode());
        List<PaasFormDataEntityExt> list = getFormListByIds4Normal(corpid, indexTypeEnum, customerIdIn, fieldList);
        for (PaasFormDataEntityExt entityExt: list){
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            CustomerInfoVO customerInfoVO = new CustomerInfoVO();
            customerInfoVO.setId(entityExt.getDataId());
            customerInfoVO.setName(data.getString(nameEnum.getAttr()));
            customerInfoVOList.add(customerInfoVO);
        }
        return customerInfoVOList;
    }
    /**
     * 获取出截至到startTime的customerIdIn，用来计算首次成交用
     * @param chartResultPojo 参数pojo
     * @return java.util.Set<java.lang.Long>
     * @author zcp
     * @date 2019/2/22 22:22
     * @since v1.0
     * @version v1.0
     */
    public Set<Long> getAgoToStartCustomerIdIn(ChartResultPojo chartResultPojo) throws XbbException {
        /*--------处理查询条件、不限制开始时间--------*/
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.customerOkQuery(chartResultPojo, false);
        //移除signTime，重新塞入新的条件
        String fieldTime = ContractEnum.getEsAttr4Keyword(ContractEnum.SIGN_TIME);
        esHelper.removeSearchCondition(boolQueryBuilder, fieldTime);
        boolQueryBuilder.filter(rangeQuery(fieldTime).lt(chartResultPojo.getStartTime()));
        IndexTypeEnum indexTypeEnum = IndexTypeEnum.IDX_SAAS_CONTRACT;
        //-------------获取出截至到startTime的customerIdIn，用来计算首次成交用
        Set<Long> agoToStartCustomerIdIn = new HashSet<>();
        ContractEnum customerIdEnum = ContractEnum.LINK_CUSTOMER;
        //设置查询字段
        List<String> fieldList = Arrays.asList(ContractEnum.getAttrConnectData(customerIdEnum));
        List<PaasFormDataEntityExt> contractList = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        for (PaasFormDataEntityExt entityExt : contractList) {
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            Long customerId = data.getLong(customerIdEnum.getAttr());
            if (customerId != null) {
                agoToStartCustomerIdIn.add(customerId);
            }
        }
        return agoToStartCustomerIdIn;
    }
    /**
     * （1）查询时间段内（start -- end）所有首次成交的客户在时间点之前（start之前）也成交过的客户id集合
     * 注：通过该方法得到的是时间段内（start -- end）所有非首次的customerId集合（在各个时间桶内不一定就是非首次）
     * （2）处理总时间区间内的首次、复购客户
     * @param chartResultPojo 参数pojo
     * @param customerOkFirstSecondPojo 首次成交、再次成交pojo
     * @return java.util.Set<java.lang.Long>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Set<Long> getAgoToStartSecondCustomerIdIn(ChartResultPojo chartResultPojo, CustomerOkFirstSecondPojo customerOkFirstSecondPojo) throws XbbException {
        //-------------获取出截至到startTime成交，且在startTime到endTime内首次成交过的customerIdIn（限制firstCids），用来处理firstCids到底是不是真的首次成交
        Set<Long> agoToStartSecondCustomerIdIn = new HashSet<>();
        //该条件必须塞入，用于查询时间段内（start -- end）所有首次成交的客户在时间点之前（start之前）也成交过的客户id集合
        Set<Long> firstCids = customerOkFirstSecondPojo.getFirstCids();
        if (CollectionsUtil.isEmpty(firstCids)) {
            //首次成交总数
            customerOkFirstSecondPojo.setFirstTotal(firstCids.size());
            //再次成交总数
            customerOkFirstSecondPojo.setSecondTotal(customerOkFirstSecondPojo.getSecondCids().size());
            return agoToStartSecondCustomerIdIn;
        }
        /*--------处理查询条件、不限制开始时间--------*/
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.customerOkQuery(chartResultPojo, false);
        //移除signTime，重新塞入新的条件
        String fieldTime = ContractEnum.getEsAttr4Keyword(ContractEnum.SIGN_TIME);
        ContractEnum customerIdEnum = ContractEnum.LINK_CUSTOMER;
        esHelper.removeSearchCondition(boolQueryBuilder, fieldTime);
        boolQueryBuilder.filter(rangeQuery(fieldTime).lt(chartResultPojo.getStartTime()));
        boolQueryBuilder.filter(termsQuery(ContractEnum.getEsAttr4Keyword(customerIdEnum), firstCids));
        IndexTypeEnum indexTypeEnum = IndexTypeEnum.IDX_SAAS_CONTRACT;
        //设置查询字段
        List<String> fieldList = Arrays.asList(ContractEnum.getAttrConnectData(customerIdEnum));
        List<PaasFormDataEntityExt> contractList = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        for (PaasFormDataEntityExt entityExt : contractList) {
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            Long customerId = data.getLong(customerIdEnum.getAttr());
            if (customerId != null) {
                agoToStartSecondCustomerIdIn.add(customerId);
            }
        }
        //处理总时间区间内的首次、再次成交
        Set<Long> secondCids = customerOkFirstSecondPojo.getSecondCids();
        //属于复购的塞入复购内
        secondCids.addAll(agoToStartSecondCustomerIdIn);
        //移除复购的（交集，即之前已经成交过的），剩余该桶内真正的首次成交
        firstCids.removeAll(agoToStartSecondCustomerIdIn);
        //首次成交总数
        customerOkFirstSecondPojo.setFirstTotal(firstCids.size());
        //再次成交总数
        customerOkFirstSecondPojo.setSecondTotal(secondCids.size());
        return agoToStartSecondCustomerIdIn;
    }

    //-------------------------------获取合同表单（id）、合同业绩分配、团队list相关-------------------------------
    /**
     * 获取合同list
     * @param contractQueryBuilder 查询条件
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/3/21 20:05
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getContractList(BoolQueryBuilder contractQueryBuilder) throws XbbException {
        //只读取出必要的字段
        String dataAmount = ContractEnum.getAttrConnectData(ContractEnum.AMOUNT);
        String dataSignTime = ContractEnum.getAttrConnectData(ContractEnum.SIGN_TIME);
        String dataCurrency = ContractEnum.getAttrConnectData(ContractEnum.CURRENCY);
        String dataGrossProfit = ContractEnum.getAttrConnectData(ContractEnum.GROSS_PROFIT);
        String dataCashProfit = ContractEnum.getAttrConnectData(ContractEnum.CASH_PROFIT);
        String dataSignPerson = ContractEnum.getAttrConnectData(ContractEnum.SIGN_PERSON);
        List<String> fieldList = Arrays.asList("id", "dataId", dataAmount, dataSignTime, dataCurrency, dataGrossProfit, dataCashProfit, dataSignPerson);
        List<PaasFormDataEntityExt> list = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT, contractQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        return list;
    }

    /**
     * 从ES内获取
     * 因为应收款、回款单的币种与相应合同的币种一致，所以要先获取合同list
     * @param corpid 公司id
     * @param contractIdIn 合同id数组
     * @return java.util.List<com.xbongbong.pro.statistic.pojo.vo.ContractInfoVO>
     * @throws XbbException
     * @author zcp
     * @date 2019/2/28 17:05
     * @since v1.0
     * @version v1.0
     */
    public List<ContractInfoVO> getContractListByIds(String corpid, Collection<Long> contractIdIn) throws XbbException {
        List<ContractInfoVO> contractInfoVoList = new ArrayList<>();
        if (contractIdIn == null || contractIdIn.size() == 0) {
            return contractInfoVoList;
        }
        //只读取出必要的字段
        ContractEnum nameEnum = ContractEnum.NAME;
//        ContractEnum currencyEnum = ContractEnum.UNIT;
        ContractEnum customerIdEnum = ContractEnum.LINK_CUSTOMER;
        String dataName = ContractEnum.getAttrConnectData(nameEnum);
//        String dataCurrency = ContractEnum.getAttrConnectData(currencyEnum);
        String dataCustomerId = ContractEnum.getAttrConnectData(customerIdEnum);
        List<String> fieldList = Arrays.asList("id", "dataId", dataName, dataCustomerId);
        List<PaasFormDataEntityExt> list = getFormListByIds4Normal(corpid, IndexTypeEnum.IDX_SAAS_CONTRACT, contractIdIn, fieldList);
        for (PaasFormDataEntityExt entityExt: list){
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            ContractInfoVO contractInfoVO = new ContractInfoVO();
            contractInfoVO.setId(entityExt.getDataId());
            contractInfoVO.setCustomerId(data.getLong(customerIdEnum.getAttr()));
            contractInfoVO.setName(data.getString(nameEnum.getAttr()));
//            contractInfoVO.setCurrency(data.getLong(currencyEnum.getAttr()));
            contractInfoVoList.add(contractInfoVO);
        }
        return contractInfoVoList;
    }
    /**
     * 获取合同业绩分配list
     * @param corpid 强制塞入公司id，防止忘记塞入公司id
     * @param perforQueryBuilder 入参，查询条件（注，封装条件时一定要把corpid条件置入）
     * @return java.util.List<com.xbongbong.saas.domain.entity.ContractPerformanceEntity>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/6 15:46
     * @since v1.0
     * @version v1.0
     */
    public List<ContractPerformanceEntity> getContractPerformanceList(String corpid, BoolQueryBuilder perforQueryBuilder) throws XbbException {
        //chartFindSheetPojo为null，外部处理过perforQueryBuilder
        return getContractPerformanceList(corpid, perforQueryBuilder, null);
    }
    /**
     * 获取合同业绩分配list -- chartFindSheetPojo不为空，则塞入其中合同id条件，并进行模拟分页查询
     * @param corpid 强制塞入公司id，防止忘记塞入公司id
     * @param perforQueryBuilder 入参，查询条件（注，封装条件时一定要把corpid条件置入）
     * @param chartFindSheetPojo 如果该参数不为空，则需要把其中的合同id参数塞入：未增加该参数前，是已经在外部把合同id条件塞入perforQueryBuilder内
     * @return java.util.List<com.xbongbong.saas.domain.entity.ContractPerformanceEntity>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<ContractPerformanceEntity> getContractPerformanceList(String corpid, BoolQueryBuilder perforQueryBuilder, ChartFindSheetPojo chartFindSheetPojo) throws XbbException {
        perforQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        // TODO: 2019/3/6 合同业绩分配的字典枚举还未创建
        String userIdAttr = ContractPerformanceSubFormEnum.getAttrConnectData(ContractPerformanceSubFormEnum.USER_ID);
        String contractIdAttr = ContractPerformanceSubFormEnum.getAttrConnectData(ContractPerformanceSubFormEnum.CONTRACT_ID);
        String rateAttr = ContractPerformanceSubFormEnum.getAttrConnectData(ContractPerformanceSubFormEnum.RATE);
        String userNameAttr = ContractPerformanceSubFormEnum.getAttrConnectData(ContractPerformanceSubFormEnum.USER_NAME);
        //只读取出必要的字段
        List<String> fieldList = Arrays.asList("id", userIdAttr, contractIdAttr, rateAttr, userNameAttr);
        List<PaasFormDataEntity> list = new ArrayList<>();
        //chartFindSheetPojo不为空，则塞入其中合同id条件，并进行模拟分页查询
        if (Objects.nonNull(chartFindSheetPojo)) {
            List<Long> contractIdIn = chartFindSheetPojo.getContractIdIn();
            if (CollectionUtils.isNotEmpty(contractIdIn)) {
                for (int i = 0; i < contractIdIn.size(); i+=PaasConstant.ES_MAX_PAGE_SIZE) {
                    int toIndex = i +  PaasConstant.ES_MAX_PAGE_SIZE;
                    if(toIndex > contractIdIn.size() ){
                        toIndex = contractIdIn.size() ;
                    }
                    List<Long> newList = contractIdIn.subList(i, toIndex);
                    if(CollectionsUtil.isNotEmpty(newList)){
                        //移除合同id条件，重新塞入新的条件
                        esHelper.removeSearchCondition(perforQueryBuilder, contractIdAttr);
                        //合同业绩分配直接用实体即可
                        perforQueryBuilder.filter(termsQuery(contractIdAttr, newList));
                        List<PaasFormDataEntity> perList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT_PERFORMANCE, perforQueryBuilder, PaasFormDataEntity.class, fieldList);
                        list.addAll(perList);
                    }
                }
            }
        } else {
            list = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT_PERFORMANCE, perforQueryBuilder, PaasFormDataEntity.class, fieldList);
        }
        return transferSubFormHelper.transferFormDataToContractPerformanceList(list);
    }
    /**
     * 根据业绩分配得到合同id
     * @param performanceList 已经限制过分配人和签订人的业绩分配list
     * @return java.util.Set<java.lang.Long>
     * @author zcp
     * @date 2019/5/2 15:12
     * @since v1.0
     * @version v1.0
     */
    public Set<Long> getContractIdsFromPerformance(List<ContractPerformanceEntity> performanceList) {
        //被分配业绩的合同ids
        Set<Long> contractIdIn = new HashSet<>();
        for (ContractPerformanceEntity entity : performanceList) {
            contractIdIn.add(entity.getContractId());
        }
        return contractIdIn;
    }

    /**
     * 得到合同业绩分配信息，构建出合同id与分配信息的对应关系
     * @param corpid 公司id
     * @param contractIdIn 合同id
     * @return java.util.Map<java.lang.Long,java.lang.String> key为合同id；value为分配信息，格式为“张三(80.0%),test(20.0%)” 
     * @throws XbbException
     * @author zcp
     * @date 2019/5/2 16:49
     * @since v1.0
     * @version v1.0
     */
    public Map<Long, String> getCidPerforInfoMap(String corpid, Set<Long> contractIdIn) throws XbbException {
        if (contractIdIn == null || contractIdIn.size() == 0) {
            return new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        }
        BoolQueryBuilder performanceQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid);
        //此时查询业绩分配实体，需要把限制过签订时间、分配人的合同ids条件塞入
        performanceQueryBuilder.filter(termsQuery(ContractPerformanceSubFormEnum.getAttrConnectData(ContractPerformanceSubFormEnum.CONTRACT_ID), contractIdIn));
        List<ContractPerformanceEntity> performanceList = getContractPerformanceList(corpid, performanceQueryBuilder);
        Map<String, String> userMap = new HashMap<>(performanceList.size());
        for (ContractPerformanceEntity contractPerformanceEntity : performanceList) {
            String userId = contractPerformanceEntity.getUserId();
            String userName = contractPerformanceEntity.getUserName();
            userMap.put(userId, userName);
        }
        //将含有离职的员工后面添加"(离职)"标记
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<String> userIdIn = userMap.keySet();
        param.put("userIdIn", userIdIn);
        param.put(ParameterConstant.LEAVE_MARKS, BasicConstant.ONE);
        param.put("corpid", corpid);
        List<UserEntity> userList = userModel.findEntitys(param);
        for (UserEntity userEntity : userList) {
            userMap.put(userEntity.getUserId(), userEntity.getName());
        }

        //合同id -- 下属内该合同分配的业绩实体合集
        Map<Long, String> cidPerforInfoMap = new HashMap<>(performanceList.size());
        for (ContractPerformanceEntity entity : performanceList) {
            Long contractId = entity.getContractId();
            String userName = cidPerforInfoMap.get(entity.getContractId());
            //归属人（所占比例）
            String belongNameStr = cidPerforInfoMap.get(contractId);
            String name = userMap.get(entity.getUserId());
            if (!StringUtil.isEmpty(belongNameStr)) {
                userName += "," + name;
            } else {
                userName = name;
            }
            //员工（所占比例）
            userName += "(" + entity.getRate() + "%)";
            cidPerforInfoMap.put(contractId, userName);
        }
        return cidPerforInfoMap;
    }

    /**
     * 获得回款单的业绩分配比例
     *
     * @param corpid 公司id
     * @param contractIdIn 合同id集合
     * @param childList 子和有父无子的回款单
     * @param parentList 父和有父无子的回款单
     * @return java.util.Map<java.lang.Long,java.lang.String>
     * @throws
     * @author 徐俊杰
     * @date 2019/12/16 11:26
     * @since v1.0
     */
    Map<Long, String> paymentSheetIdPersonInfoMap(String corpid, Set<Long> contractIdIn, List<PaasFormDataEntityExt> childList, List<PaasFormDataEntityExt> parentList) throws XbbException {
        //处理合同Id-(员工Id-比率的map)的map
        if (contractIdIn == null || contractIdIn.size() == 0) {
            return new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        }
        BoolQueryBuilder performanceQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid);
        //此时查询业绩分配实体，需要把限制过签订时间、分配人的合同ids条件塞入
        performanceQueryBuilder.filter(termsQuery(ContractPerformanceSubFormEnum.getAttrConnectData(ContractPerformanceSubFormEnum.CONTRACT_ID), contractIdIn));
        List<ContractPerformanceEntity> performanceList = getContractPerformanceList(corpid, performanceQueryBuilder);
        Map<String, String> userMaps = new HashMap<>(performanceList.size());
        for (ContractPerformanceEntity contractPerformanceEntity : performanceList) {
            String userId = contractPerformanceEntity.getUserId();
            String userName = contractPerformanceEntity.getUserName();
            userMaps.put(userId, userName);
        }
        //将含有离职的员工后面添加"(离职)"标记
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<String> userIdIn = userMaps.keySet();
        param.put("userIdIn", userIdIn);
        param.put(ParameterConstant.LEAVE_MARKS, BasicConstant.ONE);
        param.put("corpid", corpid);
        List<UserEntity> userList = userModel.findEntitys(param);
        for (UserEntity userEntity : userList) {
            userMaps.put(userEntity.getUserId(), userEntity.getName());
        }
        Map<Long, Map<String, Double>> contractIdUserIdRateMap = new HashMap<>(performanceList.size());
        Map<String, String> userIdNameMap = new HashMap<>(performanceList.size());
        for (ContractPerformanceEntity entity : performanceList) {
            String userName = userMaps.get(entity.getUserId());
            Long contractId = entity.getContractId();
            String userId = entity.getUserId();
            Double rate = entity.getRate();
            Map<String, Double> userIdRateMap = contractIdUserIdRateMap.get(contractId);
            userIdRateMap = userIdRateMap == null ? new HashMap<>(16) : userIdRateMap;
            userIdRateMap.put(userId, rate);
            contractIdUserIdRateMap.put(contractId, userIdRateMap);
            //员工Id-员工名字map
            userIdNameMap.put(userId, userName);
        }
        String contractAttr = PaymentSheetEnum.CONTRACT.getAttr();
        String paymentAttr = PaymentSheetEnum.PAYMENT.getAttr();
        String typeAttr = PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();

        //处理uuId-(合同Id-回款金额的map)的map
        Map<String, Map<String, Double>> uuIdcIdpIdAmountMap = new HashMap<>(childList.size());
        for (PaasFormDataEntityExt item : childList) {
            String uuid = item.getUuid();
            JSONObject data = item.getData();
            //一个uuid对应多个合同id
            Map<String, Double> cIdpIdAmountMap = uuIdcIdpIdAmountMap.get(uuid);
            cIdpIdAmountMap = cIdpIdAmountMap == null ? new HashMap<>(16) : cIdpIdAmountMap;
            //合同id
            JSONArray contractArray = data.getJSONArray(contractAttr);
            //子回款单仅关联一个合同，合同id和应收款id作为key（contractId_paymentId）
            String contractId = contractArray.getString(0);
            JSONArray paymentArray = data.getJSONArray(paymentAttr);
            String paymentId = "";
            if (paymentArray != null && !Objects.equals(paymentArray.size(), 0)) {
                paymentId = paymentArray.getString(0);
            }
            String cIdpId = contractId + "_" + paymentId;
            //金额
            String type = data.getString(typeAttr);
            Double amount = FundHelp.getSheetAmount4PaymentSheet(data, type);
            cIdpIdAmountMap.put(cIdpId, amount);
            uuIdcIdpIdAmountMap.put(uuid, cIdpIdAmountMap);
        }

        //父回款单id-info的map
        Map<Long, String> parentIdInfoMap = new HashMap<>(parentList.size());
        for (PaasFormDataEntityExt item : parentList) {
            String uuid = item.getUuid();
            Map<String, Double> cIdpIdAmountMap = uuIdcIdpIdAmountMap.get(uuid);
            if (cIdpIdAmountMap == null) {
                continue;
            }
            JSONObject data = item.getData();
            String type = data.getString(typeAttr);
            Double amount = FundHelp.getSheetAmount4PaymentSheet(data, type);
            //员工id-父回款单下所有合同的员工所占比率的map
            Map<String, Double> newUserIdRateMap = new HashMap<>(16);
            //一个回款单父id对应多个合同，一个合同对应多个员工
            for (Map.Entry<String, Double> map : cIdpIdAmountMap.entrySet()) {
                Double partAmount = map.getValue();
                //partAmount是合同对应的回款单金额，amount是父回款单对应的金额
                double contractRate = 1;
                try {
                    contractRate = Arith.div(partAmount, amount, 4);
                } catch (IllegalArgumentException ignored) {
                }
                String cIdpId = map.getKey();
                Long contractId = Long.valueOf(cIdpId.split("_")[0]);
                //员工id-业绩分配比率的map
                Map<String, Double> userIdRateMap = contractIdUserIdRateMap.get(contractId);
                for (Map.Entry<String, Double> userMap : userIdRateMap.entrySet()) {
                    String userId = userMap.getKey();
                    Double rate = userMap.getValue();
                    Double userRate = newUserIdRateMap.get(userId);
                    if (userRate != null) {
                        userRate += Arith.mul(rate, contractRate);
                    } else {
                        userRate = Arith.mul(rate, contractRate);
                    }
                    userRate = Arith.div(userRate, 1, 2);
                    if (userRate > 99.98) {
                        userRate = 100D;
                    }
                    newUserIdRateMap.put(userId, userRate);
                }
            }
            //封装父回款单id-info的map
            StringBuilder info = new StringBuilder();
            for (Map.Entry<String, Double> map : newUserIdRateMap.entrySet()) {
                String userId = map.getKey();
                String userName = userIdNameMap.get(userId);
                Double rate = map.getValue();
                if (!StringUtil.isEmpty(info.toString())) {
                    info.append(",");
                }
                info.append(userName).append("(").append(rate).append("%)");
            }

            Long dataId = item.getDataId();
            parentIdInfoMap.put(dataId, info.toString());
        }
        return parentIdInfoMap;
    }

    /**
     * 团队主负责人list
     * @param chartResultBasePojo 统计参数pojo
     * @return java.util.List<com.xbongbong.saas.domain.entity.ContractUserEntity>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/6 14:49
     * @since v1.0
     * @version v1.0
     */
    public List<ContractUserEntity> getContractUserList(ChartResultBasePojo chartResultBasePojo) throws XbbException {
        BoolQueryBuilder contractUserQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
        contractUserQueryBuilder.filter(termQuery(ContractTeamEnum.getEsAttr(ContractTeamEnum.IS_MAIN), BasicConstant.MAIN_USER));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            // TODO: 2019/3/3 合同负责人底层无，需表单组处理底层
            contractUserQueryBuilder.filter(termsQuery(ContractTeamEnum.getEsAttr(ContractTeamEnum.USER_ID), chartResultBasePojo.getUserIdIn()));
        }
        List<ContractUserEntity> contractUserList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT_TEAM, contractUserQueryBuilder, ContractUserEntity.class, null);
        return contractUserList;
    }

    //-------------------------------获取回款相关-------------------------------
    /**
     * 查询时间范围内的回款单，并为查询业绩分配（限制组织架构）和查询合同（处理多币种、排除非统计合同）做准备
     * 注：2020/11/26处理前该方法有入参perforQueryBuilder，且对其塞入了contractIdSet信息。因为这样直接查询id可能过长，优化为不在此处塞入，而是在查询的时候基于contractIdSet模拟分页查询出所有数据。【合同id信息都放入ChartFindSheetPojo内，因此原调用该方法的都需要在接手到该ChartFindSheetPojo后，针对合同id条件做相应处理】
     * @param sheetQueryBuilder 封装回款单查询条件
     * @param packageFlag 是否封装cidSheetListMap信息
     * @return com.xbongbong.pro.statistic.result.pojo.ChartFindSheetPojo
     * @throws XbbException
     * @author zcp
     * @date 2019/3/1 15:06
     * @since v1.0
     * @version v1.0
     * @update 2020/11/26 by zcp 子表单改造发现contractIdSet过长造成查询问题处理
     */
    public ChartFindSheetPojo findSheet(BoolQueryBuilder sheetQueryBuilder, boolean packageFlag) throws XbbException {
        //只读取出必要的字段
        PaymentSheetEnum amountEnum = PaymentSheetEnum.WRITE_OFF_AMOUNT;
        PaymentSheetEnum contractIdEnum = PaymentSheetEnum.CONTRACT;
        PaymentSheetEnum paymentTimeEnum = PaymentSheetEnum.PAYMENT_TIME;
        String dataAmount = PaymentSheetEnum.getAttrConnectData(amountEnum);
        String dataContractId = PaymentSheetEnum.getAttrConnectData(contractIdEnum);
        String dataPaymentTime = PaymentSheetEnum.getAttrConnectData(paymentTimeEnum);
        List<String> fieldList = Arrays.asList("id", "dataId", dataAmount, dataContractId, dataPaymentTime);
        List<PaasFormDataEntityExt> sheetList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, sheetQueryBuilder, PaasFormDataEntityExt.class, fieldList);

        //合同id--回款相关信息list（1个合同对应n个回款，每个回款的回款时间、回款金额不一定相同）
        Map<Long, List<EntityInfoVO>> cidSheetListMap = new HashMap<>(sheetList.size());
        Set<Long> contractIdSet = new HashSet<>(sheetList.size());
        contractIdSet.add(-1L);
        for (PaasFormDataEntityExt entityExt : sheetList) {
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            List<Long> cidList = JsonHelperUtil.parseArray(data.getString(contractIdEnum.getAttr()), Long.class);
            if (CollectionsUtil.isEmpty(cidList)) {
                continue;
            }
            contractIdSet.addAll(cidList);
            if (!packageFlag) {
                continue;
            }
            for (int i = 0; i < cidList.size(); i++) {
                Long contractId = cidList.get(i);
                List<EntityInfoVO> sList = cidSheetListMap.getOrDefault(contractId, new ArrayList<>());
                EntityInfoVO entityInfoVO = new EntityInfoVO();
                entityInfoVO.setAmount(data.getDouble(amountEnum.getAttr()));
                entityInfoVO.setTime(data.getInteger(paymentTimeEnum.getAttr()));
                sList.add(entityInfoVO);
                cidSheetListMap.put(contractId, sList);
            }
        }
        List<Long> contractIdIn = new ArrayList<>(contractIdSet);
        ChartFindSheetPojo chartFindSheetPojo = new ChartFindSheetPojo(cidSheetListMap, contractIdIn);
        return chartFindSheetPojo;
    }

    //-------------------------------获取产品分类、产品list相关-------------------------------
    /**
     * 获取产品分类列表（及其子分类）
     *
     * @param corpid     公司id
     * @param categoryId 产品分类id
     * @return java.util.List<com.xbongbong.saas.domain.entity.ProductCategoryEntity>
     * @author 徐俊杰
     * @date 2019/3/27 16:28
     * @since v1.0
     */
    public List<ProductCategoryEntity> getProductCategoryListById(String corpid, Long categoryId) {
        //产品分类实体列表
        //获取产品分类实体以及其路由（子分类）
        ProductCategoryEntity productCategoryEntity = productCategoryModel.getByKey(categoryId, corpid);
        //查询图表分类的参数
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        String router = productCategoryEntity == null ? "|-1|" : productCategoryEntity.getRouter();
        param.put("corpid", corpid);
        param.put("del", DelEnum.NORMAL.getDel());
        if (!StringUtil.isEmpty(router)) {
            param.put("routerLike", router);
        }
        return productCategoryModel.findEntitys(param);
    }

    /**
     * 以产品Id列表获取产品列表（非模糊查询、通过产品ids、未限制del）
     *
     * @param corpid      公司Id
     * @param productIdIn 产品Id列表
     * @param page 页码
     * @param pageSize 每页个数
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException findByScroll抛出异常
     * @author 徐俊杰
     * @date 2019/3/6 11:06
     * @since v1.0
     */
    public XbbAggregatedPage<PaasFormDataEntity> getProductListByPids(String corpid, Collection<Long> productIdIn, Integer page, Integer pageSize) throws XbbException {
        List<String> fieldList = productFieldList();
        BoolQueryBuilder productQueryBuilder = chartQueryHelp.boolQueryBuilder4BaseByIds(corpid, DelEnum.NORMAL.getDel(), productIdIn, IndexTypeEnum.IDX_SAAS_PRODUCT);
        return esHelper.findByPage(IndexTypeEnum.IDX_SAAS_PRODUCT, productQueryBuilder, PaasFormDataEntity.class, fieldList, page, pageSize, null);
    }

    /**
     * 以产品Id列表获取产品列表（非模糊查询、通过产品ids、未限制del）
     *
     * @param corpid 公司Id
     * @param productIdIn 产品Id列表
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException 查询产品抛出的异常
     * @author 徐俊杰
     * @date 2019/4/23 14:08
     * @since v1.0
     */
    private List<PaasFormDataEntityExt> getProductListByPids(String corpid, Collection<Long> productIdIn) throws XbbException {
        List<String> fieldList = productFieldList();
        return getFormListByIds4All(corpid, IndexTypeEnum.IDX_SAAS_PRODUCT, productIdIn, fieldList);
    }

    /**
     * 获取产品列表（以产品分类id列表查询产品列表）
     *
     * @param corpid       公司id
     * @param categoryIdIn 分类id列表
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException 查询产品时抛出的异常
     * @author 徐俊杰
     * @date 2019/3/27 14:57
     * @since v1.0
     */
    public List<PaasFormDataEntityExt> getProductListByCids(String corpid, Collection<Long> categoryIdIn) throws XbbException {
        if (categoryIdIn == null || categoryIdIn.size() == 0) {
            return new ArrayList<>();
        }
        //筛选条件
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(corpid);
        String productCategoryId = ProductEnum.getEsAttr4Keyword(ProductEnum.CATEGORY_ID);
        boolQueryBuilder.filter(termsQuery(productCategoryId, categoryIdIn));
        //需要查询的字段
        List<String> fieldList = productFieldList();
        return esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }

    /**
     * 查询产品的成本及库存数量
     *
     * @param chartResultPojo corpid
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException 查询产品抛出的异常
     * @author 徐俊杰
     * @date 2019/3/31 10:56
     * @since v1.0
     */
    public List<PaasFormDataEntityExt> getProductList4Cost(ChartResultPojo chartResultPojo) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.productCostQuery(chartResultPojo);
        //需要查询的data内字段
        List<String> fieldList = new ArrayList<>();
        fieldList.add("id");
        fieldList.add("dataId");
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.COST));
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.STOCK));
        //获取产品信息
        return esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }

    /**
     * 获取子产品所有父产品对应的产品编号
     * 注:子产品使用父产品的编号
     *
     * @param corpid     公司Id
     * @param parentIdIn 父产品IdSet
     * @return java.util.Map<java.lang.Long,java.lang.String> 父级产品id--产品编号map
     * @author 徐俊杰
     * @date 2019/3/6 14:57
     * @since v1.0
     */
    public Map<Long, String> getParentIdNoMap(String corpid, Collection<Long> parentIdIn) throws XbbException {
        //父级产品id--产品编号
        Map<Long, String> parentIdNoMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //父级产品id 国军：产品编号要用父级产品的编号（子级产品的编号即父级产品编号）
        if (parentIdIn.size() == BasicConstant.ZERO) {
            return parentIdNoMap;
        }
        //查出该批产品（productIdIn）对应的父产品（parentIdIn）信息列表
        List<PaasFormDataEntityExt> parentList = getProductListByPids(corpid, parentIdIn);
        String productNoAttr = ProductEnum.PRODUCT_NO.getAttr();
        for (PaasFormDataEntityExt entity : parentList) {
            // TODO: 2019/4/5  
            parentIdNoMap.put(entity.getDataId(), JsonHelperUtil.getJSONObject(entity.getData()).getString(productNoAttr));
        }
        return parentIdNoMap;
    }

    public ProductFieldPojo getProductFieldPojo(String filePrefix,String attr, String corpid) throws XbbException {
        ProductFieldPojo productFieldPojo = new ProductFieldPojo();
        String value = paasRedisHelper.getValue(filePrefix, corpid);
        ProductFieldPojo costField = new ProductFieldPojo();
        ProductFieldPojo stockField = new ProductFieldPojo();
        ProductFieldPojo priceField = new ProductFieldPojo();
        ProductFieldPojo unitField = new ProductFieldPojo();
        if (Objects.isNull(value)) {
            PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByBusinessTypeNoEnable(XbbRefTypeEnum.PRODUCT.getCode(), corpid);
            if (Objects.isNull(paasFormExplainEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }
            List<FieldAttrEntity> fieldAttrEntityList = JSONArray.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
            if (Objects.isNull(fieldAttrEntityList)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }
            Map<String, FieldAttrEntity> fieldAttrEntityMap = fieldAttrEntityList.stream().collect(Collectors.toMap(FieldAttrEntity::getAttr, fieldAttrEntity -> fieldAttrEntity));
            List<String> setCacheAttrList = new ArrayList<>();
            setCacheAttrList.add(ProductEnum.COST.getAttr());
            setCacheAttrList.add(ProductEnum.STOCK.getAttr());
            setCacheAttrList.add(ProductEnum.PRICE.getAttr());
            setCacheAttrList.add(ProductEnum.UNIT.getAttr());
            FieldAttrEntity stockFieldAttrEntity = fieldAttrEntityMap.getOrDefault(ProductEnum.STOCK.getAttr(), new FieldAttrEntity());
            for (String cacheAttr : setCacheAttrList) {
                FieldAttrEntity fieldAttrEntity = fieldAttrEntityMap.get(cacheAttr);
                if (Objects.isNull(fieldAttrEntity)) {
                    continue;
                }
                if (Objects.equals(cacheAttr, ProductEnum.COST.getAttr())) {
                    costField.buildData(fieldAttrEntity);
                    if (Objects.equals(fieldAttrEntity.getAttr(),attr)){
                        productFieldPojo = costField;
                    }
                    paasRedisHelper.setValue(RedisPrefixConstant.PRODUCT_COST, corpid, costField, RedisTimeConstant.LONG_DURATION);
                } else if (Objects.equals(cacheAttr, ProductEnum.STOCK.getAttr())) {
                    stockField.buildData(fieldAttrEntity);
                    if (Objects.equals(fieldAttrEntity.getAttr(),attr)){
                        productFieldPojo = stockField;
                    }
                    paasRedisHelper.setValue(RedisPrefixConstant.PRODUCT_STOCK, corpid, stockField, RedisTimeConstant.LONG_DURATION);
                } else if (Objects.equals(cacheAttr, ProductEnum.PRICE.getAttr())) {
                    priceField.buildData(fieldAttrEntity);
                    if (Objects.equals(fieldAttrEntity.getAttr(),attr)){
                        productFieldPojo = priceField;
                    }
                    paasRedisHelper.setValue(RedisPrefixConstant.PRODUCT_PRICE, corpid, priceField, RedisTimeConstant.LONG_DURATION);
                } else if (Objects.equals(cacheAttr, ProductEnum.UNIT.getAttr())) {
                    unitField.buildData(fieldAttrEntity);
                    setUnitCache(unitField, fieldAttrEntity, corpid, stockFieldAttrEntity);
                    if (Objects.equals(fieldAttrEntity.getAttr(),attr)){
                        productFieldPojo = unitField;
                    }
                    paasRedisHelper.setValue(RedisPrefixConstant.PRODUCT_UNIT, corpid, unitField, RedisTimeConstant.LONG_DURATION);
                }
            }
        } else {
            productFieldPojo = JSONObject.parseObject(value, ProductFieldPojo.class);
        }
        return productFieldPojo;
    }

    private void setUnitCache(ProductFieldPojo unitField, FieldAttrEntity entity, String corpid, FieldAttrEntity stockFieldPojo) throws XbbException{
        if (Objects.equals(stockFieldPojo.getIntegerOnly(), 1)) {
            stockFieldPojo.setAccuracy(0);
        }
        unitField.setSingleItems(entity.getItems());
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put(ParameterConstant.CORPID, corpid);
        params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        List<ProductUnitRateEntity> productUnitRateEntityList = productUnitRateModel.findEntitys(params);
        params.put("orderByStr", " sort DESC ");
        List<ProductUnitGroupEntity> productUnitGroupEntityList = productUnitGroupModel.findEntitys(params);
        Map<Long, List<ProductUnitRateEntity>> productUnitRateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        productUnitRateEntityList.forEach(item -> {
            Long groupId = item.getGroupId();
            List<ProductUnitRateEntity> list = productUnitRateMap.getOrDefault(groupId, new ArrayList<>());
            list.add(item);
            productUnitRateMap.put(groupId, list);
        });
        List<MultiUnitItemPoJo> multiItems = new ArrayList<>();
        List<UnitItemPoJo> itemDataPoJoList = new ArrayList<>();
        productUnitGroupEntityList.forEach(item -> {
            Long groupId = item.getId();
            if (productUnitRateMap.containsKey(groupId)) {
                MultiUnitItemPoJo multiUnitItemPoJo = new MultiUnitItemPoJo();
                multiUnitItemPoJo.setText(item.getName());
                multiUnitItemPoJo.setValue(groupId);
                List<ProductUnitRateEntity> list = productUnitRateMap.get(groupId);
                List<UnitItemPoJo> itemPoJoList = new ArrayList<>(list.size());
                list.forEach(unitEntity -> {
                    if (Objects.equals(unitEntity.getIsBase(), 1)) {
                        multiUnitItemPoJo.setBaseName(unitEntity.getName());
                        multiUnitItemPoJo.setBaseValue(unitEntity.getId());
                    }
                    UnitItemPoJo unitItemPoJo = new UnitItemPoJo(groupId, unitEntity.getName(), unitEntity.getId(), unitEntity.getIsBase(), unitEntity.getRate(), unitEntity.getSort());
                    unitItemPoJo.setAccuracy(stockFieldPojo.getAccuracy());
                    itemPoJoList.add(unitItemPoJo);
                });
                multiUnitItemPoJo.setItemDataPoJoList(itemPoJoList);
                multiItems.add(multiUnitItemPoJo);
                itemDataPoJoList.addAll(itemPoJoList);
            }
        });
        unitField.setMultiItems(multiItems);
        unitField.setMultiItemDataPoJoList(itemDataPoJoList);
    }

    /**
     * 获取产品单位选项
     *
     * @param corpid
     * @return
     * @throws XbbException
     */
    public List<ItemPoJo> getProductBaseItems(String corpid) throws XbbException{
        ProductFieldPojo productFieldPojo = getProductFieldPojo(RedisPrefixConstant.PRODUCT_UNIT, ProductEnum.UNIT.getAttr(), corpid);
        List<ItemPoJo> unitItems = new ArrayList<>(productFieldPojo.getSingleItems());
        List<MultiUnitItemPoJo> multiItems = productFieldPojo.getMultiItems();
        List<ItemPoJo> multiItemsPojo = new ArrayList<>();
       for (MultiUnitItemPoJo multiUnitItemPoJo : multiItems) {
           ItemPoJo itemPoJo = new ItemPoJo();
           itemPoJo.setValue(multiUnitItemPoJo.getValue());
           itemPoJo.setText(multiUnitItemPoJo.getBaseName());
           multiItemsPojo.add(itemPoJo);
       }
        unitItems.addAll(multiItemsPojo);
        return unitItems;
    }

    /**
     * 获取产品单位选项
     *
     * @param corpid
     * @return
     * @throws XbbException
     */
    public Map<String, String> getProductUnitItemMap(String corpid) throws XbbException{
        List<ItemPoJo> productItems = getProductBaseItems(corpid);
        return productItems.stream().collect(Collectors.toMap(itemPoJo -> Objects.toString(itemPoJo.getValue(), ""), ItemPoJo::getText));
    }

    /**
     * 获取业务单位选项
     *
     * @param corpid
     * @return
     * @throws XbbException
     */
    public List<ItemPoJo> getBusinessUnitItems(String corpid) throws XbbException{
        ProductFieldPojo productFieldPojo = getProductFieldPojo(RedisPrefixConstant.PRODUCT_UNIT, ProductEnum.UNIT.getAttr(), corpid);
        List<UnitItemPoJo> multiItemDataPoJoList = productFieldPojo.getMultiItemDataPoJoList();
        List<ItemPoJo> businessItemsPojo = new ArrayList<>(productFieldPojo.getSingleItems());
        try {
            BeanUtil.copyPropertiesList(multiItemDataPoJoList, businessItemsPojo, ItemPoJo.class);
        } catch (Exception e) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return businessItemsPojo;
    }

    /**
     * 获取业务单位选项
     *
     * @param corpid
     * @return
     * @throws XbbException
     */
    public Map<String, String> getBusinessUnitItemMap(String corpid) throws XbbException{
        List<ItemPoJo> businessUnitItems = getBusinessUnitItems(corpid);
        return businessUnitItems.stream().collect(Collectors.toMap(item -> Objects.toString(item.getValue(), ""), ItemPoJo::getText));
    }

    /**
     * 获取产品编号
     * 注:子产品使用父产品的编号
     *
     * @param parentIdNoMap 父产品id--产品编号map
     * @param product       产品data
     * @return java.lang.String
     * @author 徐俊杰
     * @date 2019/3/6 16:09
     * @since v1.0
     */
    public String getProductNo(Map<Long, String> parentIdNoMap, JSONObject product) {
        String productNo;
        String parentIdAttr = ProductEnum.PARENT_ID.getAttr();
        String productNoAttr = ProductEnum.PRODUCT_NO.getAttr();
        Long parentId = product.getLong(parentIdAttr);
        if (!Objects.equals(parentId, 0L)) {
            //父产品id不为0，则产品编号从父产品获取
            String parentNo = parentIdNoMap.get(parentId);
            productNo = StringUtil.isEmpty(parentNo) ? "" : parentNo;
        } else {
            productNo = product.getString(productNoAttr);
        }
        return productNo;
    }

    //-------------------------------获取生产单相关-------------------------------
    /**
     * 获取生产单list （限制计划完成时间、负责人）
     * @param chartResultPojo
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/10 21:02
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getProductionOrderListByTeam(ChartResultPojo chartResultPojo) throws XbbException {
        String fieldTime = ProductionOrderEnum.getEsAttr4Keyword(ProductionOrderEnum.PLAN_TIME);
        //限定时间、生产单负责人
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.productionOrderQuery4Team(chartResultPojo, fieldTime);

        List<String> fieldList = Arrays.asList(FieldTypeEnum.DATAID.getAlias(), ProductionOrderEnum.getAttrConnectData(ProductionOrderEnum.PRODUCT), ProductionOrderEnum.getAttrConnectData(ProductionOrderEnum.PRODUCT_NUM), ProductionOrderEnum.getAttrConnectData(ProductionOrderEnum.PRODUCT_INSTOCK_NUM));
        /*--------此时查出的生产单ids限制了时间、负责人--------*/
        //筛选生产单id（因为关联表无时间、无userId，故筛选时间和筛选部门(员工)只能根据生产单）
        List<PaasFormDataEntityExt> list = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCTION_ORDER, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        return list;
    }

    //-------------------------------获取采购、采购产品、采购退货相关-------------------------------
    /**
     * 获取采购合同list
     * @param chartResultPojo
     * @param fieldList
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/9 19:43
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getPurchaseList(ChartResultPojo chartResultPojo, List<String> fieldList) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.purchaseQuery(chartResultPojo);
        return esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PURCHASE, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }
    /**
     * 采购退货list
     * @param chartResultPojo
     * @param fieldList
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/10 10:19
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getReturnedPurchaseList(ChartResultPojo chartResultPojo, List<String> fieldList) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.returnedPurchaseQuery(chartResultPojo);
        return esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_RETURNED_PURCHASE, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }

    //-------------------------------获取入库、入库产品相关-------------------------------
    /**
     * 获取入库单列表：限制了入库时间、限制了负责人
     * @param chartResultPojo
     * @param fieldList
     * @return java.util.List<com.xbongbong.saas.domain.entity.InstockProductEntity>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/8 16:01
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getInstockList(ChartResultPojo chartResultPojo, List<String> fieldList) throws XbbException {
        //限制入库时间
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.instockQuery4Manages(chartResultPojo);
        return esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_INSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }
    /**
     * 获取入库产品成本sum（入库id集合）
     *
     * @param chartResultPojo corpid
     * @param instockIdIn     入库id集合
     * @return java.util.List<com.xbongbong.saas.domain.entity.InstockProductEntity>
     * @author 徐俊杰
     * @date 2019/3/30 14:10
     * @since v1.0
     */
    public List<InstockProductEntity> getInstockProductsCostSum(ChartResultPojo chartResultPojo, Collection<Long> instockIdIn) {
        if (instockIdIn == null || instockIdIn.size() == 0) {
            return new ArrayList<>();
        }
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //公司id
        String corpid = chartResultPojo.getCorpid();
        param.put("corpid", corpid);
        param.put("del", DelEnum.NORMAL.getDel());
        //入库id集合
        param.put("instockIdIn", instockIdIn);
        return instockProductModel.getCostSum(param);
    }
    /**
     * 获取入库产品销量sum
     * @param corpid 公司id
     * @param instockIdIn 入库id集合
     * @return java.lang.Double
     * @throws
     * @author zcp
     * @date 2019/4/16 10:35
     * @since v1.0
     * @version v1.0
     */
    public Double getInstockProductNumSum(String corpid, Collection<Long> instockIdIn) {
        if (instockIdIn == null || instockIdIn.size() == 0) {
            return 0D;
        }
        Map<String, Object> param = new HashMap<>(3);
        param.put("corpid", corpid);
        param.put("del", DelEnum.NORMAL.getDel());
        //入库id集合
        param.put("instockIdIn", instockIdIn);
        return instockProductModel.getProductNumSum(param);
    }

    //-------------------------------获取出库、出库产品相关-------------------------------
    /**
     * 获取出库单列表（不限制出库类型）：限制了出库时间、限制了负责人
     * @param chartResultPojo
     * @param fieldList
     * @return java.util.List<com.xbongbong.paas.domain.entity.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/8 20:50
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getOutstockList(ChartResultPojo chartResultPojo, List<String> fieldList) throws XbbException {
        //限制入库时间，限制负责人
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.outstockQuery4Manages(chartResultPojo);
        return esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }
    /**
     * 获取出库单id列表：限制了出库时间、限制了负责人，根据typeIn是否为null来决定是否限制出库类型
     * @param chartResultPojo 参数pojo
     * @param typeIn 出库类型枚举 @see OutstockTypeEnum
     * @return java.util.Set<java.lang.Long>
     * @throws XbbException
     * @author zcp
     * @date 2019/4/9 16:08
     * @since v1.0
     * @version v1.0
     */
    public Set<Long> getOutstockIdIn4Types(ChartResultPojo chartResultPojo, List<Integer> typeIn) throws XbbException {
        //限制入库时间，限制负责人，限制出库类型
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.outstockQuery4Types(chartResultPojo, typeIn);
        List<PaasFormDataEntityExt> outstockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, Collections.singletonList(FieldTypeEnum.DATAID.getAlias()));
        //出库单id数组
        Set<Long> outstockIdIn = new HashSet<>();
        for (PaasFormDataEntityExt entityExt : outstockList) {
            outstockIdIn.add(entityExt.getDataId());
        }
        return outstockIdIn;
    }
    /**
     * 获取入库产品销量sum
     * @param corpid 公司id
     * @param outstockIdIn 出库id集合
     * @return java.lang.Double
     * @throws
     * @author zcp
     * @date 2019/4/16 10:35
     * @since v1.0
     * @version v1.0
     */
    public Double getOutstockProductNumSum(String corpid, Collection<Long> outstockIdIn) {
        if (outstockIdIn == null || outstockIdIn.size() == 0) {
            return 0D;
        }
        Map<String, Object> param = new HashMap<>(3);
        param.put("corpid", corpid);
        param.put("del", DelEnum.NORMAL.getDel());
        //出库id集合
        param.put("outstockIdIn", outstockIdIn);
        return outstockProductModel.getProductNumSum(param);
    }

    //-------------------------------获取工单相关-------------------------------
    /**
     * 工单的id集合
     *
     * @param chartResultPojo 内部存系统指标统计时需要的参数
     * @return java.util.Set<java.lang.Long>
     * @throws XbbException findByScroll的异常
     * @author 徐俊杰
     * @date 2019/4/16 11:09
     * @since v1.0
     */
    public Set<Long> workOrderIdSet(ChartResultPojo chartResultPojo, String fieldTime, Integer flowStatus) throws XbbException {
        //查询条件
        BoolQueryBuilder workOrderQuery = chartQueryHelp.workOrderQuery4Base(chartResultPojo, fieldTime, flowStatus, null);
        List<String> fieldList = Arrays.asList("id", "dataId");
        //查询
        List<WorkOrderEntity> workOrderList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_WORK_ORDER, workOrderQuery, WorkOrderEntity.class, fieldList);
        //组装id集合
        Set<Long> workOrderIdSet = new HashSet<>();
        //防止拿idSet查询时为空
        workOrderIdSet.add(-1L);
        for (WorkOrderEntity workOrder : workOrderList) {
            Long dataId = workOrder.getDataId();
            if (null == dataId) {
                continue;
            }
            workOrderIdSet.add(dataId);
        }
        return workOrderIdSet;
    }

    /**
     * 预计时间平均值和实际时间平均值
     *
     * @param chartResultPojo 内部存系统指标统计时需要的参数
     * @return com.alibaba.fastjson.JSONObject
     * @author 徐俊杰
     * @date 2019/4/17 14:40
     * @since v1.0
     */
    public JSONObject expectedAndActualTimeAvg(ChartResultPojo chartResultPojo)  throws XbbException{
        String expectedTime = WorkOrderForStatisticsEnum.EXPECTED_TIME.getSaasAttr();
        String actualTime = WorkOrderForStatisticsEnum.ACTUAL_TIME.getSaasAttr();
        String fieldTime = WorkOrderEnum.COMPLETE_TIME.getSaasAttr();
        BoolQueryBuilder workOrderQuery = chartQueryHelp.workOrderQuery4FinishTime(chartResultPojo, fieldTime);
        List<ValuesSourceAggregationBuilder> params = esHelper.twoAverageAggParam(expectedTime, actualTime);
        return esHelper.twoAverage(IndexTypeEnum.IDX_SAAS_WORK_ORDER, workOrderQuery, params);
    }

    //-------------------------------全景图相关-------------------------------
    /**
     * scroll方式查询不支持排序，所以用折中处理方式：排序分页查询，然后正序倒序查一次（此处全景只读取出第一个和最后一个即可）
     * @param panoramaPojo 全景图参数pojo
     * @param sortOrder 排序
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/6/22 21:30
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> panoramaCustomer4SortedContractListByPage(PanoramaPojo panoramaPojo, SortOrder sortOrder) throws XbbException {
        //ES获取客户的合同
        //不排除特定合同状态，限制负责人，且不限制时间
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.panoramaQuery4Contract(panoramaPojo);
        return panoramaCustomer4SortedContractListByPage(sortOrder, boolQueryBuilder);
    }

    /**
     * scroll方式查询不支持排序，所以用折中处理方式：排序分页查询，然后正序倒序查一次（此处全景只读取出第一个和最后一个即可）
     * @param sortOrder 排序
     * @param boolQueryBuilder 查询条件
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @date 2019/6/22 21:49
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> panoramaCustomer4SortedContractListByPage(SortOrder sortOrder, BoolQueryBuilder boolQueryBuilder) throws XbbException {
        //按照签订时间降序
        List<SortBuilder> sortBuilderList = Collections.singletonList(new FieldSortBuilder(ContractEnum.getAttrConnectData(ContractEnum.SIGN_TIME)).order(sortOrder));
        //分页查询：默认查出1条即可
        int page = 1;
        int pageSize = 1;
        XbbAggregatedPage esEntities = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_CONTRACT, boolQueryBuilder, PaasFormDataEntityExt.class, null, page, pageSize, sortBuilderList);
        List<PaasFormDataEntityExt> list = new ArrayList<>();
        if (esEntities != null) {
            list = esEntities.getContent();
        }
        return list;
    }

    /**
     * 得到sheetList关联的红冲回款单列表
     * @param corpid 公司id
     * @param sheetList 回/付款单集合
     * @param indexTypeEnum es索引
     * @param dataAttrSheetType 回/付款单类型attr（已经拼上"data."）
     * @param dataAttrOrigin 关联原始回/付款单attr（已经拼上"data."）
     * @param redCodeList 红冲回/付款单code集合
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getLinkRedSheetList(String corpid, List<PaasFormDataEntityExt> sheetList, IndexTypeEnum indexTypeEnum, String dataAttrSheetType, String dataAttrOrigin, List<String> redCodeList) throws XbbException {
        Set<Long> sheetIds = new HashSet<>();
        for (PaasFormDataEntityExt entityExt : sheetList) {
            sheetIds.add(entityExt.getId());
        }
        if (sheetIds.size() == 0) {
            return sheetList;
        }
        //查询出sheetIds关联的所有红冲回/付款单
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        //除子之外
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotChildCodeList()));
        //只获取红冲的回/付款单
        boolQueryBuilder.filter(termsQuery(dataAttrSheetType, redCodeList));
        //关联了原始回/付款单sheetIds
        boolQueryBuilder.filter(termsQuery(dataAttrOrigin, sheetIds));
        //除sheetIds之外：批量删除时，防止关联红冲回/付款单重复（比如，要删除的数据里面已经有该红冲回/付款单，如果通过原单再读取出一份，这样要删除的数据里面可能有两个重复的红冲回/付款单）
        boolQueryBuilder.mustNot(termsQuery(StringConstant.DATA_ID, sheetIds));
        //此处fieldList传null，因为删除回/付款单要关联更新很多业务，要查询出的字段比较多
        List<PaasFormDataEntityExt> redSheetList = getFormList(corpid, boolQueryBuilder, indexTypeEnum, null);
        if (redSheetList != null) {
            //把关联红冲回/付款单都塞入
            sheetList.addAll(redSheetList);
        }
        return redSheetList;
    }

    /**
     * 查询出除了allDelIdList之外，且回/付款时间有效（已核销、预收/付款核销）关联了paymentIds的回/付款单（无父子、父回/付款单）
     * @param corpid 公司id
     * @param allDelIdList 所有要删除的回/付款单id集合（因为重置更新回/付款计划的时间时，需要查询除allDelIdList之外且关联了paymentIds的回/付款单）
     * @param paymentIds 回/付款计划id集合
     * @param indexTypeEnum es索引
     * @param dataAttrSheetType 回/付款单类型attr（已经拼上"data."）
     * @param dataAttrPlan 关联回/付款计划attr（已经拼上"data."）
     * @param notBadCodeList 非坏账的codeList
     * @return java.util.Map<java.lang.Long,java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Map<Long, List<PaasFormDataEntityExt>> getSheetListMap(String corpid, Collection<Long> allDelIdList, Set<Long> paymentIds, IndexTypeEnum indexTypeEnum, String dataAttrSheetType, String dataAttrPlan, List<String> notBadCodeList) throws XbbException {
        //查询出除了allDelIdList之外关联了paymentIds的回/付款单（无父子、父回/付款单）
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        //除子之外
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotChildCodeList()));
        //非坏账
        boolQueryBuilder.filter(termsQuery(dataAttrSheetType, notBadCodeList));
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        //关联了回/付款计划paymentIds
        boolQueryBuilder.filter(termsQuery(dataAttrPlan, paymentIds));
        //除allDelIdList之外
        boolQueryBuilder.mustNot(termsQuery(StringConstant.DATA_ID, allDelIdList));
        String dataAttrTime = PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.PAYMENT_TIME);
        String attrPlan = PaymentSheetEnum.PAYMENT.getAttr();
        if (Objects.equals(indexTypeEnum, IndexTypeEnum.IDX_SAAS_PAYPLAN_SHEET)) {
            dataAttrTime = PaySheetEnum.getAttrConnectData(PaySheetEnum.REAL_PAY_TIME);
            attrPlan = PaySheetEnum.PAY_PLAN.getAttr();
        }
        List<String> fieldList = Arrays.asList(StringConstant.DATA_ID, dataAttrTime, dataAttrPlan);
        List<PaasFormDataEntityExt> sheetList = getFormList(corpid, boolQueryBuilder, indexTypeEnum, fieldList);
        //计划id-该计划对应的付款单list
        Map<Long, List<PaasFormDataEntityExt>> sheetMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<PaasFormDataEntityExt> singleSheetList;
        for (PaasFormDataEntityExt sheetEntity : sheetList) {
            List<Long> pidArr = getJsonArrOrDefaultFromFormData(sheetEntity.getData(), attrPlan, new JSONArray()).toJavaList(Long.class);
            if (pidArr.size() == 0) {
                continue;
            }
            for (Long paymentId : pidArr) {
                if (sheetMap.containsKey(paymentId)) {
                    singleSheetList = sheetMap.get(paymentId);
                } else {
                    singleSheetList = new ArrayList<>();
                }
                singleSheetList.add(sheetEntity);
                sheetMap.put(paymentId, singleSheetList);
            }
        }
        return sheetMap;
    }

    //-------------------------------private方法-------------------------------
    /**
     * 需要查询的data内字段
     *
     * @return java.util.List<java.lang.String>
     * @author 徐俊杰
     * @date 2019/3/10 13:46
     * @since v1.0
     */
    private List<String> productFieldList() {
        //需要查询的data内字段
        List<String> fieldList = new ArrayList<>();
        fieldList.add("id");
        fieldList.add("dataId");
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.NAME));
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.PRODUCT_NO));
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION));
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION_LINK_TEXT));
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.UNIT));
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.CATEGORY_ID));
        fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.PARENT_ID));
        //对应原来的advanced_mode字段
        return fieldList;
    }


    /**
     * 查询模板名称映射
     * @param corpid
     * @param formIds
     * @return
     */
    public Map<Long, String> getNameMap(String corpid, List<Long> formIds) {
        if (formIds.isEmpty()) {
            return new HashMap<>();
        }
        Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        map.put("corpid", corpid);
        map.put("idIn", formIds);
        map.put("columns", "id,name");
        List<PaasFormEntityExt> paasFormEntityExtList = paasFormModel.list(map);
        Map<Long, String> nameMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormEntityExt entityExt : paasFormEntityExtList) {
            nameMap.put(entityExt.getId(), entityExt.getName());
        }
        return nameMap;
    }

    /**
     *
     * @param corpid
     * @param formId
     * @return
     */
    public PaasFormEntity getForm(String corpid, Long formId) {
        return paasFormModel.getByFormId(formId, corpid);
    }

}
